package test;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.ReferenceCountUtil;

import java.io.IOException;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.CharsetUtil;
import java.net.Socket;

public class netty {
  public void connect(){
    EventLoopGroup group = new NioEventLoopGroup(1);
    Bootstrap b = new Bootstrap();
    try {
      b.group(group)
          .channel(NioSocketChannel.class)
          .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
              ch.pipeline().addLast(new ClientHandler());
            }
          });
      ChannelFuture f = b.connect("127.0.0.1", 8888).sync();
      f.channel().closeFuture().sync();  // 固定写法 监听channel关闭 同步阻塞主线程 否则无法还未接受到服务器返回的消息程序就已经结束了
    } catch (InterruptedException e) {
      e.printStackTrace();
    }finally {
      group.shutdownGracefully();
    }
  }

  public static void main(String[] args) throws IOException {
    netty client = new netty();
    client.connect();
    client.start();
  }

  class ClientHandler extends ChannelInboundHandlerAdapter{
    // channel初始化后就向服务器发送消息
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
      ByteBuf buf  = Unpooled.copiedBuffer("hello".getBytes());
      ctx.writeAndFlush(buf);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      System.out.println("server return!!!");
      ByteBuf buf = null;
      try {
        buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.getBytes(buf.readerIndex(),bytes); // 从可读的位置开始，读满整个bytes
        System.out.println(new String(bytes));
      }finally {
        if(buf != null)
          ReferenceCountUtil.release(buf);
      }
    }
    // 所有异常都在这里处理
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
      ctx.close(); // 一般情况下出现异常就关闭该channel
    }
  }

  private int port = 8888;

  // 服务器启动
  public void start(){
    // 两个group 相当于两个线程池
    NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);  // 只负责连接
    NioEventLoopGroup workerGroup = new NioEventLoopGroup(2); // workerGroup会建立一个监听器，可以有自己的处理handle
    ServerBootstrap b = new ServerBootstrap(); // server启动前的配置
    b.group(bossGroup,workerGroup)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new Handle());
          }
        });

    try {
      ChannelFuture f = b.bind(port).sync();
      f.channel().closeFuture().sync();
    }catch (Exception e){
      e.printStackTrace();
    }finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }

}

class Handle extends ChannelInboundHandlerAdapter{
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("Channel Read!!");
    ByteBuf buf = (ByteBuf)msg;
    String s = buf.toString(CharsetUtil.UTF_8);
    s = "server " + s;
    System.out.println(buf.toString(CharsetUtil.UTF_8));
    ByteBuf buf1 = Unpooled.copiedBuffer(s.getBytes());
    ctx.writeAndFlush(buf1);
    super.channelRead(ctx, msg);
  }

  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("Channel Active!");
  }

  // 所有异常都在这里处理
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    ctx.close();  // 一般情况下出现异常就关闭该连接
  }
}
